/* The Haskell Research Compiler */
/*
 * Redistribution and use in source and binary forms, with or without modification, are permitted 
 * provided that the following conditions are met:
 * 1.   Redistributions of source code must retain the above copyright notice, this list of 
 * conditions and the following disclaimer.
 * 2.   Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _PLSR_TAGGED_INT32_
#define _PLSR_TAGGED_INT32_

/* Defining PLSR_TAGGED_INT32_TEST will cause several non-static
 * test functions to be defined which are convenient for inspecting
 * the code generated by different compilers.
 */

/* The package operations are defined in terms of three types.
 *  TaggedInt: the user defined tagged ptr/int type
 *  APType: the user defined ap (ptr) type
 *  TaggedInt32: the 32 bit tagged integer type.
 *  required:  sizeof(TaggedInt) == sizeof(APType) >= sizeof(int32)
 *
 *  Implementation specific assumption 1: we assume that for values within the range
 *  of TaggedInt32, casting to and from TTaggedInt preserves value.  This is guaranteed if 
 *  TaggedInt is a signed integer type with at least as much precision as TaggedInt32, but 
 *  implementation defined if TaggedInt is a ptr type.
 *
 *  Implementation specific assumption 2: we assume that casting between
 *  TaggedInt and APType is value preserving.  C99 says that pointer
 *  casts are implementation defined.
 *
 *  Implementation specific assumption 3: we assume (and check) that UINT32_MAX = 2^32-1.
 *  It's possible that this can be relaxed somewhat, but the proofs would need to be 
 *  checked.
 * 
 *  Implementation specific assumption 4: the non-portable version assumes some 
 *  implementation defined behavior, most notably two's complement representation, 
 *  and that >> on signed integers is implemented as arithmetic right shift.
 */

/* Commentary on integer representations and operations.
 * 
 * Notation.  
 * 
 * Implementation defined behavior means that a given implementation 
 * is free to choose what to do, but must do something predictable and documented.
 *
 * Undefined behavior means that the meaning of the program is completely unspecified.
 * The compiler is free to reject such a program, or to optimize as if the behavior
 * cannot happen, or to cause a runtime error.  It is *not* required to give a 
 * predictable/repeatable semantics to the behavior.
 *
 * Relying on implementation defined behavior could cause portability problems between
 * compilers/platforms, but should give predictable results on a given compiler/platform.
 *
 * Relying on undefined behavior is very dangerous, since a given compiler/platform
 * may intentionally or accidentally exploit this in such a way as to make the 
 * semantics completely unpredictable.  For example, signed integer overflow is undefined.
 * This implies (for example) that the test (x + 1) < x can be treated as false for
 * signed positive integer x, since either x+1 does not overflow (in which case the 
 * result is false) or else x+1 does overflow (in which case the result is undefined 
 * and the program can behave arbitrarily). There are anecdotal reports of some 
 * compilers exploiting properties such as this.
 * 
 * Representation.
 * 
 * The c99 spec leaves signed and unsigned integer representations fairly
 * implementation defined.  Most notably, it allows for arbitrary padding bits,
 * with essentially arbitrary values.  For a given precision, it imposes
 * minimum ranges of representable numbers, but not maximum ranges.  It allows
 * for several different encodings of signed integers.  In  practice, all of 
 * the platforms we care about will use two's complement arithmetic, with 
 * (semantically) no padding.  So while it might be desirable in general to
 * avoid relying on properties of the encoding, the result in most cases
 * is well-defined for a given compiler/platform.  The header <stdint.h>
 * defines precise two's complement integer types with no padding which
 * would be well-suited to this use.  However, this is not available in the
 * VC99 libraries which we are using.
 *
 * Note that the unsigned char type representation is completely well-defined.
 *
 * Operations.
 *
 * Generally speaking, most operations on unsigned integers are fairly
 * completely defined.  Operations on signed integers however are 
 * frequently implementation defined or undefined in particular cases.
 *
 * Some specific relevant cases.
 *
 * Conversion from signed to unsigned is well-defined.
 * Conversion from unsigned to signed is implementation defined when
 *  the value is not representable in the new type.
 * Left shift is well-defined on unsigned.
 * Left shift is well-defined on positive signed for which no overflow
 * occurs.  Otherwise it is UNDEFINED.
 * Right shift is well-defined on unsigned.
 * Right shift is well-defined on positive signed, and implementation
 *  defined on negative signed.
 * For all arithmetic operations, overflow/underflow on unsigned is 
 *  well-defined.
 * For all arithmetic operations, overflow/underflow on signed is
 *  UNDEFINED.
 * Logical (and/or/xor) operations are well-defined on both signed
 *  and unsigned integers.  This is less useful than it seems however,
 *  since the representations are implementation defined.
 *
 * Key points:
 * UNDEFINED: left shift on signed integers
 * UNDEFINED: overflow/underflow on signed integers
 */

/* There are two implementations provided here, chosen between by the 
 * P_TAGGED_INT32_PORTABLE flag.  All of the differences between the
 * implementations are restricted to the pLsrTaggedInt32InternalXXX
 * operations.  Both implementations avoid relying on undefined 
 * properties.  The portable version also avoids implementation
 * defined properties, at some expense in the quality of the
 * generated code.
 *
 * The generated code can be inspected by defining PLSR_TAGGED_INT32_TEST
 * and inspecting the assembly code for the test functions defined at the 
 * bottom.
 * 
 * Implementation 1 (portable).  
 * 
 * We represent signed integers x in the range -2^30 <= x < 2^30 as
 * 2*x + 1.  This allows them to be distinguished from pointers under
 * the implementation specific assumption that pointers are always even.
 * Tagging and untagging are done via well-defined arithmetic operations.
 *
 * Implementation 2 (non-portable).
 *
 * We represent signed integers x in the range -2^30 <= x < 2^30 as
 * 2*x & 0x1.  This allows them to be distinguished from pointers
 * under various implementation specific assumptions about the 
 * representation of integers (all of which should be true on all 
 * platforms we care about).
 * N.B.  Even under implementation specific assumptions, x >> 1 != x/2.
 */


/************************************************************************/
/********** This section defines the core internal operations ***********/
/************************************************************************/

/* We begin by defining the basic internal operations on tagged 32 bit integers. */
typedef sint32 PlsrTaggedInt32;

/* Some constants */
#define pLsrTaggedInt32InternalMax  (SINT32_MAX / 2)
#define pLsrTaggedInt32InternalMin  (SINT32_MIN / 2)
#define pLsrTaggedInt32InternalTwoToThe31 0x80000000
#define pLsrTaggedInt32InternalTwoToThe30 0x40000000


#if (defined(P_TAGGED_INT32_ASSUME_SMALL) || defined(P_TAGGED_INT32_ASSERT_SMALL))

/************************ Tagging **********************************/
#define pLsrTaggedInt32InternalTag(a) ((PlsrTaggedInt32) (a))

/************************ UnTagging ********************************/
#define pLsrTaggedInt32InternalUnTag(a) ((PlsrTaggedInt32) a)

/********************** Tag checking *******************************/
#define pLsrTaggedInt32InternalIsTagged(a) 1

/***************** Two operand tag checking ************************/
#define pLsrTaggedInt32InternalAreTagged(a, b) 1

/********************* Range checking *****************************/
#ifdef P_TAGGED_INT32_ASSERT_SMALL
#define pLsrTaggedInt32InternalSIntInRange(a)                           \
    ((((a) <= pLsrTaggedInt32InternalMax) &&                            \
      ((a) >= pLsrTaggedInt32InternalMin)) ||                           \
     (pLsrRuntimeError_("SInt out of range"), 0))                        
#define pLsrTaggedInt32InternalUIntInRange(a)                   \
    (((a) <= pLsrTaggedInt32InternalMax) ||                     \
     (pLsrRuntimeError_("UInt out of range"), 0))                 
#else
#define pLsrTaggedInt32InternalSIntInRange(a) 1
#define pLsrTaggedInt32InternalUIntInRange(a) 1
#endif /* P_TAGGED_INT32_ASSERT_SMALL */

#define pLsrTaggedInt32InternalSInt32InRange pLsrTaggedInt32InternalSIntInRange
#define pLsrTaggedInt32InternalUInt32InRange pLsrTaggedInt32InternalUIntInRange
#define pLsrTaggedInt32InternalSInt64InRange pLsrTaggedInt32InternalSIntInRange
#define pLsrTaggedInt32InternalUInt64InRange pLsrTaggedInt32InternalUIntInRange

/******************** Check assumptions **************************/
#define pLsrTaggedInt32InternalCheck(APType, TaggedInt)                 \
    do {                                                                \
        assert(sizeof(APType) >= sizeof(PlsrTaggedInt32));              \
        assert(sizeof(TaggedInt) >= (sizeof(APType)));                  \
    } while (0)                                                         

#else /* !P_TAGGED_INT32_ASSERT_SMALL && !P_TAGGED_INT32_ASSUME_SMALL*/
#ifdef P_TAGGED_INT32_PORTABLE


/************************ Tagging **********************************/
/* The tagged version of an integer x in the appropriate range is 2*x + 1.
 * Note that this is guaranteed not to overflow for 32 bit integers according to the 
 * c99 spec.
 */
#define pLsrTaggedInt32InternalTag(a) ((((PlsrTaggedInt32) (a)) * 2) + 1)


/************************ UnTagging ********************************/
/* The untagged version of a tagged integer x is (x-1)/2. 
 */
#define pLsrTaggedInt32InternalUnTag(a) ((((PlsrTaggedInt32) (a)) -1 )/2)


/********************** Tag checking *******************************/
/* We test if x is tagged by checking (((uint32) x) mod 2): that is, 
 * x is tagged iff it is odd.  Note that conversion to uint32 preserves parity
 * so long as UINT32_MAX + 1 is even.  The Intel C compiler generates good 
 * code for this in some contexts but not others.  
 */
#define pLsrTaggedInt32InternalIsTagged(a) (((uint32) ((PlsrTaggedInt32) (a))) % 2)


/***************** Two operand tag checking ************************/
/* We test if x and y are both tagged by computing
 * (((unsigned char) (((uint32) x) mod 2)) & ((unsigned char) (((uint32) y) mod 2)))
 * Using bitwise and avoids a double jump and produces better code.  The conversion
 * to unsigned char eliminates any padding bits.
 */
#define pLsrTaggedInt32InternalAreTagged(a, b)                          \
    (pLsrTaggedInt32InternalIsTagged(a)) &&                             \
    (pLsrTaggedInt32InternalIsTagged(b))


/********************* Range checking *****************************/

/* We can test any integer for being in range by comparing to the appropriate
 * max and min values.  However, this does not generate very good code.  This is 
 * fully portable.
 */
#define pLsrTaggedInt32InternalSIntInRange(a)                           \
    (((a) <= pLsrTaggedInt32InternalMax) && ((a) >= pLsrTaggedInt32InternalMin))
#define pLsrTaggedInt32InternalUIntInRange(a) ((a) <= pLsrTaggedInt32InternalMax)
#define pLsrTaggedInt32InternalUInt32InRange pLsrTaggedInt32InternalUIntInRange
#define pLsrTaggedInt32InternalUInt64InRange pLsrTaggedInt32InternalUIntInRange
#define pLsrTaggedInt32InternalSInt32InRange pLsrTaggedInt32InternalSIntInRange
#define pLsrTaggedInt32InternalSInt64InRange pLsrTaggedInt32InternalSIntInRange

/******************** Check assumptions **************************/
#define pLsrTaggedInt32InternalCheck(APType, TaggedInt)                 \
    do {                                                                \
        assert(sizeof(APType) >= sizeof(PlsrTaggedInt32));              \
        assert(sizeof(TaggedInt) >= (sizeof(APType)));                  \
    } while (0)                                                         


#else /* !P_TAGGED_INT32_PORTABLE && !P_TAGGED_INT32_ASSUME_SMALL && !P_TAGGED_INT32_ASSERT_SMALL */


#define pLsrTaggedInt32InternalSetLowBit(a) ((a) | 0x1)
#define pLsrTaggedInt32InternalGetLowBit(a) ((a) & 0x1)
#define pLsrTaggedInt32InternalASR(a) ((a) >> 1)


/************************ Tagging **********************************/
/* The tagged version of an integer x in the appropriate range is 2*x | 0x1.
 * Under reasonable assumptions about representation, this sets the low bit 
 * only.
 */
#define pLsrTaggedInt32InternalTag(a) \
    (pLsrTaggedInt32InternalSetLowBit(((PlsrTaggedInt32) (a)) * 2))


/************************ UnTagging ********************************/
/* The untagged version of a tagged integer x is x >> 1.  Under reasonable
 * assumptions about representation and the implementation of >>, this
 * does an arithmetic shift right.
 */
#define pLsrTaggedInt32InternalUnTag(a) \
    (pLsrTaggedInt32InternalASR((PlsrTaggedInt32) (a)))


/********************** Tag checking *******************************/
/* An integer is tagged if the low bit is set. */
#define pLsrTaggedInt32InternalIsTagged(a) \
    (pLsrTaggedInt32InternalGetLowBit((PlsrTaggedInt32) (a)))


/***************** Two operand tag checking ************************/
/* We test if x and y are both tagged by checking that both low bits are set */
#define pLsrTaggedInt32InternalAreTagged(a, b)                          \
    (pLsrTaggedInt32InternalGetLowBit((((uint32) ((PlsrTaggedInt32) (a))) & \
                                       ((uint32) ((PlsrTaggedInt32) (b))))))


/********************* Range checking *****************************/

/* We can test any integer for being in range by comparing to the appropriate
 * max and min values.  However, this does not generate very good code.  This is 
 * fully portable.
 */
#define pLsrTaggedInt32InternalSIntInRange(a)                           \
    (((a) <= pLsrTaggedInt32InternalMax) && ((a) >= pLsrTaggedInt32InternalMin))
#define pLsrTaggedInt32InternalUIntInRange(a) ((a) <= pLsrTaggedInt32InternalMax)

/* Hard to do better than this for uints */
#define pLsrTaggedInt32InternalUInt32InRange pLsrTaggedInt32InternalUIntInRange
#define pLsrTaggedInt32InternalUInt64InRange pLsrTaggedInt32InternalUIntInRange

/* For signed integer types with known precision, we can define
 * a test which generates better code as follows, under the implementation 
 * specific assumptions that 1) UINT32_MAX = 2^32-1, and 
 * 2) SINT32_MAX = 2^32-1 and 3) SINT32_MAX = -2^32 (actually, any power of
 * two).
 *
 * We test if x is in the representable range (that is -2^30 <= x < 2^30) 
 * by casting it to uint32, adding 2^30 and testing against 2^31.  See the 
 * proof below for a formal justification of this in terms of the c99 spec, 
 * but informally what this is doing is as follows:
 * The conversion to an unsigned int preserves the bit layout in two's 
 * complement.  For negative numbers, x is representable if the leading
 * two bits are 11, and for positive numbers, x is representable if
 * the leading two bits are 00.  Otherwise, the number is not representable.
 * So we wish to tell whether or not the leading bits differ.  We do this
 * by adding 2^30 to the number.  If the leading bits are 11, it overflows
 * and the leading bits are 00.  If the leading bits are 00, the leading
 * bits become 01.  In both cases, the number is now less than 2^31.  If
 * the bits differ, there is no overflow, and the leading bit will be 1.
 * Therefore the number will be greater than or equal to 2^31.
 *
 * This explanation assumes two's complement representation with a particular
 * bit order: however the proof below shows that this method is in
 * fact fully general under very limited assumptions about the range of the 
 * respective types.  These assumptions could be eliminated if we had 
 * access to the stdint.h types.
 */
#define pLsrTaggedInt32InternalSInt32InRange(a)                         \
    ((((uint32) (a)) + pLsrTaggedInt32InternalTwoToThe30) < pLsrTaggedInt32InternalTwoToThe31)
#define pLsrTaggedInt32InternalSInt64InRange(a)                         \
    ((((uint64) (a)) + pLsrTaggedInt32InternalTwoToThe30) < pLsrTaggedInt32InternalTwoToThe31)

/* Proof of correctness for range checking.
 * 
 * According the c99 spec, converting from a signed to an unsigned type of the 
 * same size:
 *  1) preserves the value if the value is representable (positive)
 *  2) is the result of repeatedly adding one more than the maximum value of
 *     the new type to the old value
 * So, assuming a is a negative sint32, then
 *    ((uint32) a) = (UINT32_MAX + 1) + a
 * For this proof, I assume that (UINT32_MAX + 1) = 2^32.  Strictly speaking,
 * I'm not sure this is guaranteed - I believe the spec permits additional 
 * range.  In practice, this isn't a concern.  I believe this proof could be 
 * generalized to relax this assumption somewhat.  The proof goes through
 * fine with any power of two for UINT32_MAX.
 *
 * For a 32 bit signed int k, -2^31 <= k < 2^31
 * Let S' = ((uint32) k) + 2^30
 * and let S = S' mod 2^32
 * Case 1: k is positive.  Then:
 *   - ((uint32) k) = k, so
 *   - S = (k + 2^30) mod 2^32
 *       = k + 2^30   (since k + 2^30 < 2^32)
 *    
 *   Suppose S < 2^31.  Then 
 *     S = k + 2^30 < 2^31 
 *         k + 2^30 < 2^30 + 2^30
 *         k        < 2^30
 *   Suppose S >= 2^31.  Then
 *     S = k + 2^30 >= 2^31 
 *         k + 2^30 >= 2^30 + 2^30
 *         k        >= 2^30
 *    
 * Case 2: k is negative (-1 >= k >= -2^31).  Then:
 *   - By the c99 spec (assuming UINT32_MAX = 2^32-1)
 *     ((uint32) k) = 2^32 + k 
 *   - So, S' = 2^32 + k + 2^30
 *
 *    Suppose k >= -2^30.  Note that
 *         S = S' mod 2^32
 *           = (2^32 + k + 2^30) mod 2^32
 *           = (k + 2^30) mod 2^32
 *    But note that 0 <= k + 2^30 < 2^30, so
 *           = (k + 2^30) 
 *    And so 
 *         S < 2^30
 *    Finally, by contrapositive:
 *         S >= 2^30 => k < -2^30
 *    So clearly
 *         S >= 2^31 => k < -2^30
 *
 *    Suppose k < -2^30.  Note that
 *         S = S' mod 2^32
 *           = (2^32 + k + 2^30) mod 2^32
 *    But note that 
 *        -2^31 + 2^30 <= k + 2^30 < -2^30 + 2^30, i.e.
 *              - 2^30 <= k + 2^30 < 0
 *    So 
 *         2^32 - 2^30 <= S'       < 2^32 + 0
 *         2^31 + 2^30 <= S'       < 2^32
 *    So 
 *         S = S' mod 2^32 = S' >= 2^31 + 2^30
 *    Finally, by contrapositive, 
 *         S < 2^31 + 2^30 => k >= -2^30
 *    So clearly
 *         S < 2^31 => k >= -2^30
 *
 * So we have:
 *   S < 2^31 => k <   2^30  (k positive)
 *   S < 2^31 => k >= -2^30  (k negative)
 * And
 *   S >= 2^31 => k >= 2^30  (k positive)
 *   S >= 2^31 => k < -2^30  (k negative)
 * 
 * So S < 2^31  => -2^30 <= k < 2^30
 *
 */

/******************** Check assumptions **************************/
#define pLsrTaggedInt32InternalCheck(APType, TaggedInt)                 \
    do {                                                                \
        assert(sizeof(APType) >= sizeof(PlsrTaggedInt32));              \
        assert(sizeof(TaggedInt) >= (sizeof(APType)));                  \
        assert(UINT32_MAX == 0xFFFFFFFF);                               \
        assert(SINT32_MIN == 0x80000000);                               \
        assert(SINT32_MAX == 0x7FFFFFFF);                               \
    } while (0)                                                         

#endif /* P_TAGGED_INT32_PORTABLE */
#endif /* P_TAGGED_INT32_ASSUME_SMALL || P_TAGGED_INT32_ASSERT_SMALL */


/************************************************************************/
/******************* This is the main implementation ********************/
/************************************************************************/

#define pLsrTaggedInt32Check pLsrTaggedInt32InternalCheck

/*********************** Conversions ************************************/

#define pLsrTaggedInt32Max (pLsrTaggedInt32InternalMax)
#define pLsrTaggedInt32Min (pLsrTaggedInt32InternalMin)
#define pLsrTaggedInt32Zero (pLsrTaggedInt32InternalTag(0))
#define pLsrTaggedInt32One (pLsrTaggedInt32InternalTag(1))
#define pLsrTaggedInt32MinusOne (pLsrTaggedInt32InternalTag(-1))


#define pLsrTaggedInt32TaggedIntIsTagged(a) (pLsrTaggedInt32InternalIsTagged(a))
#define pLsrTaggedInt32TaggedIntUnTag(a)                                \
    (assert(pLsrTaggedInt32InternalIsTagged(a)), (pLsrTaggedInt32InternalUnTag(a)))
#define pLsrTaggedInt32TaggedIntFromSmallInt32(TaggedInt, a)    \
    ((TaggedInt) (pLsrTaggedInt32InternalTag(a)))

/* The NumConv macros are statement macros. */

/* Convert a value of type sintx to the tagged int type */
#define pLsrTaggedInt32NumConvFastTaggedIntFromSIntX(TaggedInt, SIntX, apFromSIntX, dest, a) \
    do {                                                                \
        if (sizeof(SIntX) <= sizeof(sint32)) {                          \
            if (pLsrTaggedInt32InternalSInt32InRange(a))                \
                {(dest) = ((TaggedInt) (pLsrTaggedInt32InternalTag(a)));} \
            else                                                        \
                {(dest) = ((TaggedInt) (apFromSIntX(a)));}              \
        } else if (sizeof(SIntX) == sizeof(sint64)) {                   \
            if (pLsrTaggedInt32InternalSInt64InRange(a))                \
                {(dest) = ((TaggedInt) (pLsrTaggedInt32InternalTag(a)));} \
            else                                                        \
                {(dest) = ((TaggedInt) (apFromSIntX(a)));}              \
        } else {                                                        \
            if (pLsrTaggedInt32InternalSIntInRange(a))                  \
                {(dest) = ((TaggedInt) (pLsrTaggedInt32InternalTag(a)));} \
            else                                                        \
                {(dest) = ((TaggedInt) (apFromSIntX(a)));}              \
        }                                                               \
    } while (0)

/* Convert a value of the tagged int type to type sintx */
#define pLsrTaggedInt32NumConvFastSIntXFromTaggedInt(SIntX, TaggedInt, APType, sIntXFromAP, dest, a) \
    do {                                                                \
        if (pLsrTaggedInt32InternalIsTagged(a))                         \
            {(dest) = ((SIntX) (pLsrTaggedInt32InternalUnTag(a)));}     \
        else                                                            \
            {(dest) = ((sIntXFromAP((APType) (a))));}                   \
    } while (0)

/* Convert a value of type uintx to the tagged int type */
#define pLsrTaggedInt32NumConvFastTaggedIntFromUIntX(TaggedInt, UIntX, apFromUIntX, dest, a) \
    do {                                                                \
        if (sizeof(UIntX) <= sizeof(uint32)) {                          \
            if (pLsrTaggedInt32InternalUInt32InRange(a))                \
                {(dest) = ((TaggedInt) (pLsrTaggedInt32InternalTag(a)));} \
            else                                                        \
                {(dest) = ((TaggedInt) (apFromUIntX(a)));}              \
        } else if (sizeof(UIntX) == sizeof(uint64)) {                   \
            if (pLsrTaggedInt32InternalUInt64InRange(a))                \
                {(dest) = ((TaggedInt) (pLsrTaggedInt32InternalTag(a)));} \
            else                                                        \
                {(dest) = ((TaggedInt) (apFromUIntX(a)));}              \
        } else {                                                        \
            if (pLsrTaggedInt32InternalUIntInRange(a))                  \
                {(dest) = ((TaggedInt) (pLsrTaggedInt32InternalTag(a)));} \
            else                                                        \
                {(dest) = ((TaggedInt) (apFromUIntX(a)));}              \
        }                                                               \
    } while (0)

/* Convert a value of the tagged int type to type uintx */
#define pLsrTaggedInt32NumConvFastUIntXFromTaggedInt(UIntX, TaggedInt, APType, uIntXFromAP, dest, a) \
    do {                                                                \
        if (pLsrTaggedInt32InternalIsTagged(a))                         \
            {(dest) = ((UIntX) (pLsrTaggedInt32InternalUnTag(a)));}     \
        else                                                            \
            {(dest) = ((uIntXFromAP((APType) (a))));}                   \
    } while (0)

/* Convert a value of type floatx to the tagged int type */
/* This might be a bit of a hack.  Find a better way to do this?  -leaf */
#define pLsrTaggedInt32NumConvFastTaggedIntFromFloat32(TaggedInt, apFromFloat32, dest, a) \
    do {                                                                \
        if ((((float32)(sint32)a) == ((float32) a)) &&                  \
            (pLsrTaggedInt32InternalSInt32InRange((sint32)a))) {        \
            (dest) = ((TaggedInt) (pLsrTaggedInt32InternalTag((sint32)a))); \
        } else {                                                        \
            (dest) = ((TaggedInt) (apFromFloat32(a)));                  \
        }                                                               \
    } while (0)

#define pLsrTaggedInt32NumConvFastTaggedIntFromFloat64(TaggedInt, apFromFloat64, dest, a) \
    do {                                                                \
        if ((((float64)(sint32)a) == ((float64) a)) &&                  \
            (pLsrTaggedInt32InternalSInt32InRange((sint32)a))) {        \
            (dest) = ((TaggedInt) (pLsrTaggedInt32InternalTag((sint32)a))); \
        } else {                                                        \
            (dest) = ((TaggedInt) (apFromFloat64(a)));                   \
        }                                                               \
    } while (0)

/* Convert a value of the tagged int type to type floatX */
#define pLsrTaggedInt32NumConvFastFloatXFromTaggedInt(FloatX, TaggedInt, APType, floatXFromAP, dest, a) \
    do {                                                                \
        if (pLsrTaggedInt32InternalIsTagged(a))                         \
            {(dest) = ((FloatX) (pLsrTaggedInt32InternalUnTag(a)));}    \
        else                                                            \
            {(dest) = ((floatXFromAP((APType) (a))));}                  \
    } while (0)

#define pLsrTaggedInt32MkCStringFrom(TName, TaggedInt, APType, apFromSInt32, cStringFromAP) \
    static char* pLsrCStringFrom##TName (TaggedInt a) {                 \
        {                                                               \
            if(pLsrTaggedInt32InternalIsTagged(a)) {                    \
                return cStringFromAP(apFromSInt32(pLsrTaggedInt32InternalUnTag(a))); \
            } else {                                                    \
                return cStringFromAP((APType) a);                       \
            }                                                           \
        }                                                               \
    }                                                                   \

#define pLsrTaggedInt32MkFromCString(TName, TaggedInt, APType, apFromSInt32, sint32FromAP, apLessOrEqual, apFromCString) \
    static TaggedInt pLsr##TName##FromCString (char* s) {               \
        {                                                               \
            APType ap = apFromCString(s);                               \
            APType max = apFromSInt32(pLsrTaggedInt32InternalMax);      \
            APType min = apFromSInt32(pLsrTaggedInt32InternalMin);      \
            if (apLessOrEqual (min, ap) && apLessOrEqual(ap, max)) {    \
                return (TaggedInt) pLsrTaggedInt32InternalTag(sint32FromAP(ap)); \
            } else {                                                    \
                return (TaggedInt) ap;                                  \
            }                                                           \
        }                                                               \
    }                                                                   \

/**************************** Bitwise *********************************/

/* Can't make a tagged value non-taggable */
#define pLsrTaggedInt32TaggedIntBNot(TaggedInt, APType, apBNot, apFromSInt32, dest, a) \
    do {                                                                \
        if (pLsrTaggedInt32InternalIsTagged((PlsrTaggedInt32) (a))) {   \
            sint32 pLsrTaggedInt32TaggedIntBNot_tmp =                    \
                ~((sint32) (pLsrTaggedInt32InternalUnTag(a)));          \
            (dest) = ((TaggedInt) pLsrTaggedInt32InternalTag(pLsrTaggedInt32TaggedIntBNot_tmp)); \
        } else {                                                        \
            (dest) = ((TaggedInt) (apBNot((APType) a)));                \
        }                                                               \
    } while(0)

/* Most of the binary bitwise operations can't overflow into the high bits
 * and hence can use these versions. */
#define pLsrTaggedInt32InternalBitwiseSlowPathReturnNO(TaggedInt, APType, apFromSInt32, apBin, a, b) \
    do {                                                                \
        APType pLsrTaggedInt32InternalBitwiseSlowPathReturn_aA;         \
        APType pLsrTaggedInt32InternalBitwiseSlowPathReturn_bB;         \
        if (pLsrTaggedInt32InternalIsTagged(a)) {                       \
            pLsrTaggedInt32InternalBitwiseSlowPathReturn_aA =           \
                apFromSInt32(pLsrTaggedInt32InternalUnTag(a));          \
        } else {                                                        \
            pLsrTaggedInt32InternalBitwiseSlowPathReturn_aA = (APType)a; \
        }                                                               \
        if (pLsrTaggedInt32InternalIsTagged(b)) {                       \
            pLsrTaggedInt32InternalBitwiseSlowPathReturn_bB =           \
                apFromSInt32(pLsrTaggedInt32InternalUnTag(b));          \
        } else {                                                        \
            pLsrTaggedInt32InternalBitwiseSlowPathReturn_bB = (APType)b; \
        }                                                               \
        return (TaggedInt) apBin(pLsrTaggedInt32InternalBitwiseSlowPathReturn_aA, \
                                 pLsrTaggedInt32InternalBitwiseSlowPathReturn_bB); \
    } while(0)

#define pLsrTaggedInt32MkTaggedIntBitwiseBinSlowNO(TName, OpName, TaggedInt, APType, apFromSInt32, apBin, binOp) \
    static TaggedInt pLsrTaggedInt32##TName##OpName##Slow (TaggedInt a, TaggedInt b) { \
        if(pLsrTaggedInt32InternalAreTagged(a, b)) {                    \
            sint32 tmp =                                                \
                binOp(((sint32) (pLsrTaggedInt32InternalUnTag(a))),     \
                      ((sint32) (pLsrTaggedInt32InternalUnTag(b))));    \
            return (TaggedInt) pLsrTaggedInt32InternalTag(tmp);         \
        } else {                                                        \
            pLsrTaggedInt32InternalBitwiseSlowPathReturnNO(TaggedInt, APType, apFromSInt32, apBin, a, b); \
        }                                                               \
    } 


#define pLsrTaggedInt32MkTaggedIntBitwiseBinNO(TaggedInt, APType, binOp, binOpSlow, dest, a, b) \
    do {                                                                \
        if(pLsrTaggedInt32InternalAreTagged(a, b)) {                    \
            sint32 pLsrTaggedInt32MkTaggedIntBitwiseBinNO_tmp =         \
                binOp(((sint32) (pLsrTaggedInt32InternalUnTag(a))),     \
                      ((sint32) (pLsrTaggedInt32InternalUnTag(b))));    \
            (dest) = (TaggedInt) (pLsrTaggedInt32InternalTag(pLsrTaggedInt32MkTaggedIntBitwiseBinNO_tmp)); \
        } else {                                                        \
            (dest) = binOpSlow(a, b);                                   \
        }                                                               \
    } while(0)

/* Currently just use the slow version for things which might overflow into
 * the high bits.  
 */

#define pLsrTaggedInt32InternalBitwiseSlowPathReturn(TaggedInt, APType, apFromSInt32, apBin, a, b) \
    do {                                                                \
        APType pLsrTaggedInt32InternalBitwiseSlowPathReturn_aA;         \
        APType pLsrTaggedInt32InternalBitwiseSlowPathReturn_bB;         \
        if (pLsrTaggedInt32InternalIsTagged(a)) {                       \
            pLsrTaggedInt32InternalBitwiseSlowPathReturn_aA =           \
                apFromSInt32(pLsrTaggedInt32InternalUnTag(a));          \
        } else {                                                        \
            pLsrTaggedInt32InternalBitwiseSlowPathReturn_aA = (APType)a; \
        }                                                               \
        if (pLsrTaggedInt32InternalIsTagged(b)) {                       \
            pLsrTaggedInt32InternalBitwiseSlowPathReturn_bB =           \
                apFromSInt32(pLsrTaggedInt32InternalUnTag(b));          \
        } else {                                                        \
            pLsrTaggedInt32InternalBitwiseSlowPathReturn_bB = (APType)b; \
        }                                                               \
        return (TaggedInt) apBin(pLsrTaggedInt32InternalBitwiseSlowPathReturn_aA, \
                                 pLsrTaggedInt32InternalBitwiseSlowPathReturn_bB); \
    } while(0)

#define pLsrTaggedInt32MkTaggedIntBitwiseBinSlow(TName, OpName, TaggedInt, APType, apFromSInt32, apBin, binOp) \
    static TaggedInt pLsrTaggedInt32##TName##OpName##Slow (TaggedInt a, TaggedInt b) { \
        pLsrTaggedInt32InternalBitwiseSlowPathReturn(TaggedInt, APType, apFromSInt32, apBin, a, b); \
    }

#define pLsrTaggedInt32MkTaggedIntBitwiseBin(TaggedInt, APType, binOp, binOpSlow, dest, a, b) \
    do {                                                                \
        (dest) = binOpSlow(a, b);                                       \
    } while(0)


#define pLsrTaggedInt32TaggedIntBAndOp(a, b) (a & b)
#define pLsrTaggedInt32MkTaggedIntBAndSlow(TName, TaggedInt, APType, apFromSInt32, apBAnd) \
    pLsrTaggedInt32MkTaggedIntBitwiseBinSlowNO(TName, BAnd, TaggedInt, APType, apFromSInt32, \
                                               apBAnd, pLsrTaggedInt32TaggedIntBAndOp)
#define pLsrTaggedInt32TaggedIntBAnd(TaggedInt, APType, bAndSlow, dest, aa, bb) \
    pLsrTaggedInt32MkTaggedIntBitwiseBinNO(TaggedInt, APType, pLsrTaggedInt32TaggedIntBAndOp, \
                                           bAndSlow, dest, aa, bb) 

#define pLsrTaggedInt32TaggedIntBOrOp(a, b) (a | b)
#define pLsrTaggedInt32MkTaggedIntBOrSlow(TName, TaggedInt, APType, apFromSInt32, apBOr) \
    pLsrTaggedInt32MkTaggedIntBitwiseBinSlowNO(TName, BOr, TaggedInt, APType, apFromSInt32, apBOr, \
                                               pLsrTaggedInt32TaggedIntBOrOp)
#define pLsrTaggedInt32TaggedIntBOr(TaggedInt, APType, bOrSlow, dest, aa, bb) \
    pLsrTaggedInt32MkTaggedIntBitwiseBinNO(TaggedInt, APType, pLsrTaggedInt32TaggedIntBOrOp, bOrSlow, \
                                           dest, aa, bb) 

/* Can overflow */
#define pLsrTaggedInt32TaggedIntBShiftLOp(a, b) (assert(0)))
#define pLsrTaggedInt32MkTaggedIntBShiftLSlow(TName, TaggedInt, APType, apFromSInt32, apBShiftL) \
    pLsrTaggedInt32MkTaggedIntBitwiseBinSlow(TName, BShiftL, TaggedInt, APType, apFromSInt32, apBShiftL, \
                                             pLsrTaggedInt32TaggedIntBShiftLOp)
#define pLsrTaggedInt32TaggedIntBShiftL(TaggedInt, APType, bShiftLSlow, dest, aa, bb) \
    pLsrTaggedInt32MkTaggedIntBitwiseBin(TaggedInt, APType, \
                                         pLsrTaggedInt32TaggedIntBShiftLOp, bShiftLSlow, dest, aa, bb) 

/* a >> b is only defined for 0 <= b <= 31 */
#define pLsrTaggedInt32TaggedIntBShiftROp(a, b) ((b >= 0 && b < 32) ? (a >> b) : \
                                                 ((b < 0 && b > -32) ? (a << -b) : \
                                                  ((a < 0) ? -1 : 0))) 
#define pLsrTaggedInt32MkTaggedIntBShiftRSlow(TName, TaggedInt, APType, apFromSInt32, apBShiftR) \
    pLsrTaggedInt32MkTaggedIntBitwiseBinSlow(TName, BShiftR, TaggedInt, APType, apFromSInt32, apBShiftR, \
                                             pLsrTaggedInt32TaggedIntBShiftROp)
#define pLsrTaggedInt32TaggedIntBShiftR(TaggedInt, APType, bShiftRSlow, dest, aa, bb) \
    pLsrTaggedInt32MkTaggedIntBitwiseBin(TaggedInt, APType, \
                                         pLsrTaggedInt32TaggedIntBShiftROp, bShiftRSlow, dest, aa, bb) 

#define pLsrTaggedInt32TaggedIntBXorOp(a, b) (a ^ b)
#define pLsrTaggedInt32MkTaggedIntBXorSlow(TName, TaggedInt, APType, apFromSInt32, apBXor) \
    pLsrTaggedInt32MkTaggedIntBitwiseBinSlowNO(TName, BXor, TaggedInt, APType, apFromSInt32, apBXor, \
                                               pLsrTaggedInt32TaggedIntBXorOp)
#define pLsrTaggedInt32TaggedIntBXor(TaggedInt, APType, bXorSlow, dest, aa, bb) \
    pLsrTaggedInt32MkTaggedIntBitwiseBinNO(TaggedInt, APType, \
                                           pLsrTaggedInt32TaggedIntBXorOp, bXorSlow, dest, aa, bb) 

/**************************** Arithmetic *********************************/

#define pLsrTaggedInt32TaggedIntNeg(TaggedInt, APType, apNeg, apFromSInt32, dest, a) \
    do {                                                                \
        if (pLsrTaggedInt32InternalIsTagged((PlsrTaggedInt32) (a))) {   \
            sint32 pLsrTaggedInt32TaggedIntNeg_tmp =                    \
                -((sint32) (pLsrTaggedInt32InternalUnTag(a)));          \
            if ((pLsrTaggedInt32InternalSInt32InRange(pLsrTaggedInt32TaggedIntNeg_tmp))) \
                {(dest) = ((TaggedInt) pLsrTaggedInt32InternalTag(pLsrTaggedInt32TaggedIntNeg_tmp));} \
            else                                                        \
                {(dest) = ((TaggedInt) apFromSInt32(pLsrTaggedInt32TaggedIntNeg_tmp));} \
        } else {                                                        \
            (dest) = ((TaggedInt) (apNeg((APType) a)));                 \
        }                                                               \
    } while(0)

#define pLsrTaggedInt32InternalArithSlowPathReturn(TaggedInt, APType, apFromSInt32, apBin, a, b) \
    do {                                                                \
        APType pLsrTaggedInt32InternalArithSlowPathReturn_aA;           \
        APType pLsrTaggedInt32InternalArithSlowPathReturn_bB;           \
        if (pLsrTaggedInt32InternalIsTagged(a)) {                       \
            pLsrTaggedInt32InternalArithSlowPathReturn_aA =             \
                apFromSInt32(pLsrTaggedInt32InternalUnTag(a));          \
        } else {                                                        \
            pLsrTaggedInt32InternalArithSlowPathReturn_aA = (APType)a;  \
        }                                                               \
        if (pLsrTaggedInt32InternalIsTagged(b)) {                       \
            pLsrTaggedInt32InternalArithSlowPathReturn_bB =             \
                apFromSInt32(pLsrTaggedInt32InternalUnTag(b));          \
        } else {                                                        \
            pLsrTaggedInt32InternalArithSlowPathReturn_bB = (APType)b;  \
        }                                                               \
        return (TaggedInt) apBin(pLsrTaggedInt32InternalArithSlowPathReturn_aA, \
                                 pLsrTaggedInt32InternalArithSlowPathReturn_bB); \
    } while(0)

/* Addition and subtraction */
#define pLsrTaggedInt32MkTaggedIntAddSubSlow(TName, OpName, TaggedInt, APType, apFromSInt32, apBin, binOp) \
    static TaggedInt pLsrTaggedInt32##TName##OpName##Slow (TaggedInt a, TaggedInt b) { \
        if(pLsrTaggedInt32InternalAreTagged(a, b)) {                    \
            sint32 tmp =                                                \
                (((sint32) (pLsrTaggedInt32InternalUnTag(a))) binOp     \
                 ((sint32) (pLsrTaggedInt32InternalUnTag(b))));         \
            if (pLsrTaggedInt32InternalSInt32InRange(tmp)) {            \
                return (TaggedInt) pLsrTaggedInt32InternalTag(tmp);     \
            }                                                           \
            else {                                                      \
                return (TaggedInt) apFromSInt32(tmp);                   \
            }                                                           \
        } else {                                                        \
            pLsrTaggedInt32InternalArithSlowPathReturn(TaggedInt, APType, apFromSInt32, apBin, a, b); \
        }                                                               \
    } 

#define pLsrTaggedInt32MkTaggedIntAddSub(TaggedInt, APType, binOp, binOpSlow, dest, a, b) \
    do {                                                                \
        if(pLsrTaggedInt32InternalAreTagged(a, b)) {                    \
            sint32 pLsrTaggedInt32MkTaggedIntAddSub_tmp =               \
                (((sint32) (pLsrTaggedInt32InternalUnTag(a))) binOp     \
                 ((sint32) (pLsrTaggedInt32InternalUnTag(b))));         \
            if (pLsrTaggedInt32InternalSInt32InRange(pLsrTaggedInt32MkTaggedIntAddSub_tmp)) { \
                (dest) = (TaggedInt) (pLsrTaggedInt32InternalTag(pLsrTaggedInt32MkTaggedIntAddSub_tmp)); \
                continue;                                               \
            }                                                           \
        }                                                               \
        (dest) = binOpSlow(a, b);                                       \
    } while(0)

#define pLsrTaggedInt32MkTaggedIntAddSlow(TName, TaggedInt, APType, apFromSInt32, apAdd) \
    pLsrTaggedInt32MkTaggedIntAddSubSlow(TName, Add, TaggedInt, APType, apFromSInt32, apAdd, +)
#define pLsrTaggedInt32TaggedIntAdd(TaggedInt, APType, addSlow, dest, aa, bb) \
    pLsrTaggedInt32MkTaggedIntAddSub(TaggedInt, APType, +, addSlow, dest, aa, bb) 

#define pLsrTaggedInt32MkTaggedIntSubSlow(TName, TaggedInt, APType, apFromSInt32, apSub) \
    pLsrTaggedInt32MkTaggedIntAddSubSlow(TName, Sub, TaggedInt, APType, apFromSInt32, apSub, -)
#define pLsrTaggedInt32TaggedIntSub(TaggedInt, APType, subSlow, dest, aa, bb) \
    pLsrTaggedInt32MkTaggedIntAddSub(TaggedInt, APType, -, subSlow, dest, aa, bb) 


/* Multiplication */
#define pLsrTaggedInt32MkTaggedIntMulSlow(TName, TaggedInt, APType, apFromSInt32, apFromSInt64, apMul) \
    static TaggedInt pLsrTaggedInt32##TName##MulSlow (TaggedInt a, TaggedInt b) { \
        if(pLsrTaggedInt32InternalAreTagged(a, b)) {                    \
            sint64 tmp =                                                \
                (((sint64)(sint32) (pLsrTaggedInt32InternalUnTag(a))) * \
                 ((sint64)(sint32) (pLsrTaggedInt32InternalUnTag(b)))); \
            if (pLsrTaggedInt32InternalSInt64InRange(tmp)) {            \
                return (TaggedInt) pLsrTaggedInt32InternalTag(tmp);     \
            } else {                                                    \
                return (TaggedInt) apFromSInt64(tmp);                   \
            }                                                           \
        } else {                                                        \
            pLsrTaggedInt32InternalArithSlowPathReturn(TaggedInt, APType, apFromSInt32, apMul, a, b); \
        }                                                               \
    }                                                                   

/* We use inline assembly here to work around an icl limitation.  The compiler 
 * fails to turn  add64((int64) a, b) into imul (a, b) when b is a small 
 * constant.  Using assembly here probably eliminates some optimization
 * opportunities.  
 * We could use a more customized asm sequence, but defining this via
 * a saturating mutiply has the advantage of playing relatively nicely with
 * the various versions of tagging/untagging/range checking.  Note that this
 * is not a "proper" saturating multiply, since a negative result saturates
 * ot SINT32_MAX.  We're really just using SINT32_MAX as an error result here.
 */
#ifdef PLSR_GNU_ASM 
#ifdef __KNC__
/* avoid cmovo for KNC */
#define pLsrTaggedInt32InternalSInt32MulSaturating(D, A, B) 	  \
    __asm__("imul %1, %0;jno 1f;mov %2, %0;1:"                    \
            : "=r"(D)                                             \
            : "rm"(B), "rm"(SINT32_MAX), "0"(A)); 
#else
/* D, A, and B should be variables here */
/* This produces significantly better code */
#define pLsrTaggedInt32InternalSInt32MulSaturating(D, A, B)       \
    __asm__("imul %1, %0; cmovo %2, %0"                           \
            : "=r"(D)                                             \
            : "rm"(B), "rm"(SINT32_MAX), "0"(A)); 
#endif /* __KNC__ */
#else /* PLSR_GNU_ASM */
#ifdef PLSR_MS_ASM
#define pLsrTaggedInt32InternalSInt32MulSaturating(D, A, B)           \
    __asm { __asm mov eax, A                                          \
            __asm mov ecx, SINT32_MAX                                 \
            __asm imul eax, B                                         \
            __asm cmovo eax, ecx                                      \
            __asm mov D, eax                                          \
            };
#else /* PLSR_MS_ASM */
#error "Inline assembly unsupported"
#endif /* PLSR_MS_ASM */
#endif /* PLSR_GNU_ASM */

#define pLsrTaggedInt32TaggedIntMul(TaggedInt, APType, mulSlow, dest, a, b) \
    do {                                                                \
        if(pLsrTaggedInt32InternalAreTagged(a, b)) {                    \
            sint32 pLsrTaggedInt32TaggedIntMul_a = pLsrTaggedInt32InternalUnTag(a); \
            sint32 pLsrTaggedInt32TaggedIntMul_b = pLsrTaggedInt32InternalUnTag(b); \
            sint32 pLsrTaggedInt32TaggedIntMul_tmp;                     \
            pLsrTaggedInt32InternalSInt32MulSaturating(pLsrTaggedInt32TaggedIntMul_tmp, \
                                                       pLsrTaggedInt32TaggedIntMul_a, \
                                                       pLsrTaggedInt32TaggedIntMul_b);\
            /*            pLsrTaggedInt32TaggedIntMul_tmp = (((sint64) pLsrTaggedInt32TaggedIntMul_a) * */ \
            /*                                               ((sint64) pLsrTaggedInt32TaggedIntMul_b));*/ \
            if (pLsrTaggedInt32InternalSInt32InRange(pLsrTaggedInt32TaggedIntMul_tmp)) { \
                (dest) = (TaggedInt) (pLsrTaggedInt32InternalTag(pLsrTaggedInt32TaggedIntMul_tmp)); \
                continue;                                               \
            }                                                           \
        }                                                               \
        (dest) = mulSlow(a, b);                                         \
    } while(0)

/* Division */

typedef enum {
    PlsrTaggedInt32InternalDivKindT,
    PlsrTaggedInt32InternalDivKindE,
    PlsrTaggedInt32InternalDivKindF
} PlsrTaggedInt32InternalDivKind;

#define pLsrTaggedInt32InternalMkTaggedIntDivModSlow(Kind, kind, TName, TaggedInt, APType, apFromSInt32, apDivMod) \
    static void pLsrTaggedInt32##TName##DivMod##Kind##Slow (TaggedInt* q, TaggedInt* r, TaggedInt a, TaggedInt b) \
    {                                                                   \
        if(pLsrTaggedInt32InternalAreTagged(a, b)) {                    \
            sint32 ai = pLsrTaggedInt32InternalUnTag(a);                \
            sint32 bi = pLsrTaggedInt32InternalUnTag(b);                \
            sint32 qi = ai/bi;                                          \
            sint32 ri = ai%bi;                                          \
            if (kind == PlsrTaggedInt32InternalDivKindF) {              \
                if (((ri > 0) - (ri < 0)) == -((bi > 0) - (bi < 0))) {  \
                    qi--;                                               \
                    ri += bi;                                           \
                }                                                       \
            } else if (kind == PlsrTaggedInt32InternalDivKindE) {       \
                if (ri < 0) {                                           \
                    if (bi > 0) {                                       \
                        qi--;                                           \
                        ri += bi;                                       \
                    } else {                                            \
                        qi++;                                           \
                        ri -= bi;                                       \
                    }                                                   \
                }                                                       \
            }                                                           \
            /* |ai % bi| < |bi| */                                      \
            *r = (TaggedInt) (pLsrTaggedInt32InternalTag(ri));          \
            /* plsrTaggedInt32InternalMin/-1 > plsrTaggedInt32InternalMax */ \
            if (pLsrTaggedInt32InternalSInt32InRange(qi)) {             \
                *q = (TaggedInt) (pLsrTaggedInt32InternalTag(qi));      \
            } else {                                                    \
                *q = (TaggedInt) (apFromSInt32(qi));                    \
            }                                                           \
        } else {                                                        \
            APType aA;                                                  \
            APType bB;                                                  \
            if (pLsrTaggedInt32InternalIsTagged(a)) {                   \
                aA =                                                    \
                    apFromSInt32(pLsrTaggedInt32InternalUnTag(a));      \
            } else {                                                    \
                aA = (APType)a;                                         \
            }                                                           \
            if (pLsrTaggedInt32InternalIsTagged(b)) {                   \
                bB =                                                    \
                    apFromSInt32(pLsrTaggedInt32InternalUnTag(b));      \
            } else {                                                    \
                bB = (APType)b;                                         \
            }                                                           \
            apDivMod((APType*) q, (APType*) r, aA, bB);                 \
        }                                                               \
    }


#define pLsrTaggedInt32InternalTaggedIntDivMod(kind, TaggedInt, APType, apFromSInt32, divModSlow, q, r, a, b) \
    do {                                                                \
        if(pLsrTaggedInt32InternalAreTagged(a, b)) {                    \
            sint32 pLsrTaggedInt32InternalTaggedIntDivMod_ai = pLsrTaggedInt32InternalUnTag(a); \
            sint32 pLsrTaggedInt32InternalTaggedIntDivMod_bi = pLsrTaggedInt32InternalUnTag(b); \
            sint32 pLsrTaggedInt32InternalTaggedIntDivMod_qi =          \
                pLsrTaggedInt32InternalTaggedIntDivMod_ai / pLsrTaggedInt32InternalTaggedIntDivMod_bi; \
            sint32 pLsrTaggedInt32InternalTaggedIntDivMod_ri =          \
                pLsrTaggedInt32InternalTaggedIntDivMod_ai % pLsrTaggedInt32InternalTaggedIntDivMod_bi; \
                                                                        \
            if (kind == PlsrTaggedInt32InternalDivKindF) {              \
                                                                        \
                if (                                                    \
                    ((pLsrTaggedInt32InternalTaggedIntDivMod_ri > 0) -  \
                     (pLsrTaggedInt32InternalTaggedIntDivMod_ri < 0))   \
                    ==                                                  \
                    -((pLsrTaggedInt32InternalTaggedIntDivMod_bi > 0) - \
                      (pLsrTaggedInt32InternalTaggedIntDivMod_bi < 0))  \
                    )                                                   \
                    {                                                   \
                        pLsrTaggedInt32InternalTaggedIntDivMod_qi--;    \
                        pLsrTaggedInt32InternalTaggedIntDivMod_ri += pLsrTaggedInt32InternalTaggedIntDivMod_bi; \
                    }                                                   \
            } else if (kind == PlsrTaggedInt32InternalDivKindE) {       \
                if (pLsrTaggedInt32InternalTaggedIntDivMod_ri < 0) {    \
                    if (pLsrTaggedInt32InternalTaggedIntDivMod_bi > 0)  \
                        {                                               \
                            pLsrTaggedInt32InternalTaggedIntDivMod_qi--; \
                            pLsrTaggedInt32InternalTaggedIntDivMod_ri += pLsrTaggedInt32InternalTaggedIntDivMod_bi; \
                        }                                               \
                    else {                                              \
                        pLsrTaggedInt32InternalTaggedIntDivMod_qi++;    \
                        pLsrTaggedInt32InternalTaggedIntDivMod_ri -= pLsrTaggedInt32InternalTaggedIntDivMod_bi; \
                    }                                                   \
                }                                                       \
            }                                                           \
                                                                        \
            /* |ai % bi| < |bi| */                                      \
            r = (TaggedInt) (pLsrTaggedInt32InternalTag(pLsrTaggedInt32InternalTaggedIntDivMod_ri)); \
                                                                        \
            /* plsrTaggedInt32InternalMin/-1 > plsrTaggedInt32InternalMax */ \
            if (pLsrTaggedInt32InternalSInt32InRange(pLsrTaggedInt32InternalTaggedIntDivMod_qi)) { \
                q = (TaggedInt) (pLsrTaggedInt32InternalTag(pLsrTaggedInt32InternalTaggedIntDivMod_qi)); \
            } else {                                                    \
                q = (TaggedInt) (apFromSInt32(pLsrTaggedInt32InternalTaggedIntDivMod_qi)); \
            }                                                           \
                                                                        \
        } else {                                                        \
            TaggedInt PlsrTaggedInt32InternalTaggedIntDivMod_qq = NULL; \
            TaggedInt PlsrTaggedInt32InternalTaggedIntDivMod_rr = NULL; \
            divModSlow(&PlsrTaggedInt32InternalTaggedIntDivMod_qq, &PlsrTaggedInt32InternalTaggedIntDivMod_rr, a, b); \
            q = PlsrTaggedInt32InternalTaggedIntDivMod_qq;              \
            r = PlsrTaggedInt32InternalTaggedIntDivMod_rr;              \
        }                                                               \
    } while(0)

#define pLsrTaggedInt32InternalTaggedIntDiv(kind, TaggedInt, APType, apFromSInt32, divModSlow, q, a, b) \
    do {                                                                \
        TaggedInt pLsrTaggedInt32InternalTaggedIntDiv_r = NULL;         \
        pLsrTaggedInt32InternalTaggedIntDivMod(kind, TaggedInt, APType, apFromSInt32, divModSlow, q, \
                                               pLsrTaggedInt32InternalTaggedIntDiv_r, a, b); \
      } while (0)

#define pLsrTaggedInt32InternalTaggedIntMod(kind, TaggedInt, APType, apFromSInt32, divModSlow, r, a, b) \
    do {                                                                \
        TaggedInt pLsrTaggedInt32InternalTaggedIntMod_q = NULL;         \
        pLsrTaggedInt32InternalTaggedIntDivMod(kind, TaggedInt, APType, apFromSInt32, \
                                               divModSlow, pLsrTaggedInt32InternalTaggedIntMod_q, r, a, b); \
    } while (0)

#define pLsrTaggedInt32MkTaggedIntDivModTSlow(TName, TaggedInt, APType, apFromSInt32, apDivModT) \
    pLsrTaggedInt32InternalMkTaggedIntDivModSlow(T, PlsrTaggedInt32InternalDivKindT, \
                                                 TName, TaggedInt, APType, apFromSInt32, apDivModT) \
        
#define pLsrTaggedInt32TaggedIntDivModT(TaggedInt, APType, apFromSInt32, divModTSlow, q, r, a, b) \
    pLsrTaggedInt32InternalTaggedIntDivMod(PlsrTaggedInt32InternalDivKindT, \
                                           TaggedInt, APType, apFromSInt32, divModTSlow, q, r, a, b) \

#define pLsrTaggedInt32TaggedIntDivT(TaggedInt, APType, apFromSInt32, divModTSlow, q, a, b) \
    pLsrTaggedInt32InternalTaggedIntDiv(PlsrTaggedInt32InternalDivKindT, \
                                        TaggedInt, APType, apFromSInt32, divModTSlow, q, a, b) \

#define pLsrTaggedInt32TaggedIntModT(TaggedInt, APType, apFromSInt32, divModTSlow, r, a, b) \
    pLsrTaggedInt32InternalTaggedIntMod(PlsrTaggedInt32InternalDivKindT, \
                                        TaggedInt, APType, apFromSInt32, divModTSlow, r, a, b) \

#define pLsrTaggedInt32MkTaggedIntDivModESlow(TName, TaggedInt, APType, apFromSInt32, apDivModE) \
    pLsrTaggedInt32InternalMkTaggedIntDivModSlow(E, PlsrTaggedInt32InternalDivKindE, \
                                                 TName, TaggedInt, APType, apFromSInt32, apDivModE) \

#define pLsrTaggedInt32TaggedIntDivModE(TaggedInt, APType, apFromSInt32, divModESlow, q, r, a, b) \
    pLsrTaggedInt32InternalTaggedIntDivMod(PlsrTaggedInt32InternalDivKindE, \
                                           TaggedInt, APType, apFromSInt32, divModESlow, q, r, a, b) \

#define pLsrTaggedInt32TaggedIntDivE(TaggedInt, APType, apFromSInt32, divModESlow, q, a, b) \
    pLsrTaggedInt32InternalTaggedIntDiv(PlsrTaggedInt32InternalDivKindE, \
                                        TaggedInt, APType, apFromSInt32, divModESlow, q, a, b) \

#define pLsrTaggedInt32TaggedIntModE(TaggedInt, APType, apFromSInt32, divModESlow, r, a, b) \
    pLsrTaggedInt32InternalTaggedIntMod(PlsrTaggedInt32InternalDivKindE, \
                                        TaggedInt, APType, apFromSInt32, divModESlow, r, a, b) \

#define pLsrTaggedInt32MkTaggedIntDivModFSlow(TName, TaggedInt, APType, apFromSInt32, apDivModF) \
    pLsrTaggedInt32InternalMkTaggedIntDivModSlow(F, PlsrTaggedInt32InternalDivKindF, \
                                                 TName, TaggedInt, APType, apFromSInt32, apDivModF) \

#define pLsrTaggedInt32TaggedIntDivModF(TaggedInt, APType, apFromSInt32, divModFSlow, q, r, a, b) \
    pLsrTaggedInt32InternalTaggedIntDivMod(PlsrTaggedInt32InternalDivKindF, \
                                           TaggedInt, APType, apFromSInt32, divModFSlow, q, r, a, b) \

#define pLsrTaggedInt32TaggedIntDivF(TaggedInt, APType, apFromSInt32, divModFSlow, q, a, b) \
    pLsrTaggedInt32InternalTaggedIntDiv(PlsrTaggedInt32InternalDivKindF, \
                                        TaggedInt, APType, apFromSInt32, divModFSlow, q, a, b) \

#define pLsrTaggedInt32TaggedIntModF(TaggedInt, APType, apFromSInt32, divModFSlow, r, a, b) \
    pLsrTaggedInt32InternalTaggedIntMod(PlsrTaggedInt32InternalDivKindF, \
                                        TaggedInt, APType, apFromSInt32, divModFSlow, r, a, b) 

#define pLsrTaggedInt32MkTaggedIntGcd(TName, TaggedInt, APType, apFromSInt32, apLessOrEqual, sint32FromAP, apGcd) \
    static TaggedInt pLsrTaggedInt32##TName##Gcd(TaggedInt a, TaggedInt b) \
    {                                                                   \
        if(pLsrTaggedInt32InternalAreTagged(a, b)) {                    \
            sint32 ai = pLsrTaggedInt32InternalUnTag(a);                \
            sint32 bi = pLsrTaggedInt32InternalUnTag(b);                \
            if (ai<0) ai = -ai;                                         \
            if (bi<0) bi = -bi;                                         \
            while (bi != 0) {                                           \
                sint32 r = ai%bi;                                       \
                ai = bi;                                                \
                bi = r;                                                 \
            }                                                           \
            return (TaggedInt) (pLsrTaggedInt32InternalTag(ai));        \
        } else {                                                        \
            APType aA, bB;                                              \
            if (pLsrTaggedInt32InternalIsTagged(a)) {                   \
                aA = apFromSInt32(pLsrTaggedInt32InternalUnTag(a));     \
            } else {                                                    \
                aA = (APType)a;                                         \
            }                                                           \
            if (pLsrTaggedInt32InternalIsTagged(b)) {                   \
                bB = apFromSInt32(pLsrTaggedInt32InternalUnTag(b));     \
            } else {                                                    \
                bB = (APType)b;                                         \
            }                                                           \
            APType r = apGcd(aA, bB);                                   \
            APType min = apFromSInt32(pLsrTaggedInt32InternalMin);      \
            APType max = apFromSInt32(pLsrTaggedInt32InternalMax);      \
            if (apLessOrEqual (min, r) && apLessOrEqual(r, max)) {      \
                return (TaggedInt) pLsrTaggedInt32InternalTag(sint32FromAP(r)); \
            } else {                                                    \
                return (TaggedInt) r;                                   \
            }                                                           \
        }                                                               \
    }

/* Comparisons */

#define pLsrTaggedInt32MkTaggedIntCmpSlow(TName, OpName, TaggedInt, APType, apFromSInt32, apCmp, cmpOp) \
    static PlsrBoolean pLsrTaggedInt32##TName##OpName##Slow (TaggedInt a, TaggedInt b) { \
        if(pLsrTaggedInt32InternalAreTagged(a, b)) {                    \
            return (PlsrBoolean) (((PlsrTaggedInt32) a) cmpOp             \
                                 ((PlsrTaggedInt32) b));                 \
        } else {                                                        \
            APType aA, bB;                                              \
            if (pLsrTaggedInt32InternalIsTagged(a)) {                   \
                aA = apFromSInt32(pLsrTaggedInt32InternalUnTag(a));     \
            } else {                                                    \
                aA = (APType)a;                                         \
            }                                                           \
            if (pLsrTaggedInt32InternalIsTagged(b)) {                   \
                bB = apFromSInt32(pLsrTaggedInt32InternalUnTag(b));     \
            } else {                                                    \
                bB = (APType)b;                                         \
            }                                                           \
            return apCmp(aA, bB);                                       \
        }                                                               \
    }

#define pLsrTaggedInt32MkTaggedIntCmp(cmpSlow, cmpOp, dest, a, b)       \
    do {                                                                \
        if(pLsrTaggedInt32InternalAreTagged((a), (b))) {                \
            (dest) = ((PlsrBoolean) (((PlsrTaggedInt32) (a)) cmpOp        \
                                    ((PlsrTaggedInt32) (b))));           \
        } else {                                                        \
            (dest) = (cmpSlow((a), (b)));                               \
        }                                                               \
    } while(0)

#define pLsrTaggedInt32MkTaggedIntEqSlow(TName, TaggedInt, APType, apFromSInt32, apEq) \
    pLsrTaggedInt32MkTaggedIntCmpSlow(TName, Eq, TaggedInt, APType, apFromSInt32, apEq, ==) 
#define pLsrTaggedInt32TaggedIntEq(eqSlow, dest, aa, bb)        \
    pLsrTaggedInt32MkTaggedIntCmp(eqSlow, ==, dest, aa, bb)


#define pLsrTaggedInt32MkTaggedIntNeSlow(TName, TaggedInt, APType, apFromSInt32, apNe) \
    pLsrTaggedInt32MkTaggedIntCmpSlow(TName, Ne, TaggedInt, APType, apFromSInt32, apNe, !=) 
#define pLsrTaggedInt32TaggedIntNe(neSlow, dest, aa, bb) \
    pLsrTaggedInt32MkTaggedIntCmp(neSlow, !=, dest, aa, bb)

#define pLsrTaggedInt32MkTaggedIntLtSlow(TName, TaggedInt, APType, apFromSInt32, apLt) \
    pLsrTaggedInt32MkTaggedIntCmpSlow(TName, Lt, TaggedInt, APType, apFromSInt32, apLt, <) 
#define pLsrTaggedInt32TaggedIntLt(ltSlow, dest, aa, bb) \
    pLsrTaggedInt32MkTaggedIntCmp(ltSlow, <, dest, aa, bb)

#define pLsrTaggedInt32MkTaggedIntGtSlow(TName, TaggedInt, APType, apFromSInt32, apGt) \
    pLsrTaggedInt32MkTaggedIntCmpSlow(TName, Gt, TaggedInt, APType, apFromSInt32, apGt, >) 
#define pLsrTaggedInt32TaggedIntGt(gtSlow, dest, aa, bb) \
    pLsrTaggedInt32MkTaggedIntCmp(gtSlow, >, dest, aa, bb)

#define pLsrTaggedInt32MkTaggedIntLeSlow(TName, TaggedInt, APType, apFromSInt32, apLe) \
    pLsrTaggedInt32MkTaggedIntCmpSlow(TName, Le, TaggedInt, APType, apFromSInt32, apLe, <=) 
#define pLsrTaggedInt32TaggedIntLe(leSlow, dest, aa, bb) \
    pLsrTaggedInt32MkTaggedIntCmp(leSlow, <=, dest, aa, bb)

#define pLsrTaggedInt32MkTaggedIntGeSlow(TName, TaggedInt, APType, apFromSInt32, apGe) \
    pLsrTaggedInt32MkTaggedIntCmpSlow(TName, Ge, TaggedInt, APType, apFromSInt32, apGe, >=) 
#define pLsrTaggedInt32TaggedIntGe(geSlow, dest, aa, bb) \
    pLsrTaggedInt32MkTaggedIntCmp(geSlow, >=, dest, aa, bb)

/************************ Hashing **********************************/

#define pLsrTaggedInt32TaggedIntHash(TaggedInt, APType, apHash, a)      \
    ((pLsrTaggedInt32InternalIsTagged((PlsrTaggedInt32) (a))) ?         \
     pLsrSInt32Hash(pLsrTaggedInt32InternalUnTag(a)) :                  \
     apHash((APType)a))

/************************ Testing **********************************/
#ifdef PLSR_TAGGED_INT32_TEST

/************************ Tagging **********************************/
void pLsrTaggedInt32TestTag(sint32 x) {
    PlsrTaggedInt32 r = pLsrTaggedInt32InternalTag(x);
    printf("Tagged version of %d is %d\n", x, r);
}

/************************ UnTagging ********************************/
void pLsrTaggedInt32TestUnTag(PlsrTaggedInt32 x) {
    sint32 r = (sint32) pLsrTaggedInt32InternalUnTag(x);
    printf("UnTagged version of %d is %d\n", x, r);
}

/********************** Tag checking *******************************/
void pLsrTaggedInt32TestIsTagged(PlsrTaggedInt32 x) {
    if (pLsrTaggedInt32InternalIsTagged(x)) {
        printf("%d is tagged", x);
    } else {
        printf("%d is not tagged", x);
    }
}

/***************** Two operand tag checking ************************/
void pLsrTaggedInt32TestAreTagged(PlsrTaggedInt32 x, PlsrTaggedInt32 y) {
    if (pLsrTaggedInt32InternalAreTagged(x, y)) {
        printf("%d and %d are tagged", x, y);
    } else {
        printf("%d and/or %d is not tagged", x, y);
    }
}


/********************* Range checking *****************************/
void pLsrTaggedInt32TestSIntInRange(sint32 x, sint64 y) {
    if (pLsrTaggedInt32InternalSInt32InRange(x)) {
        printf("in range");
    } else {
        printf("not in range");
    }
    if (pLsrTaggedInt32InternalSInt64InRange(y)) {
        printf("in range");
    } else {
        printf("not in range");
    }
}

void pLsrTaggedInt32TestUIntInRange(uint32 x, uint64 y) {
    if (pLsrTaggedInt32InternalUInt32InRange(x)) {
        printf("in range");
    } else {
        printf("not in range");
    }
    if (pLsrTaggedInt32InternalUInt64InRange(y)) {
        printf("in range");
    } else {
        printf("not in range");
    }
}
#endif /* PLSR_TAGGED_INT32_TEST */

#endif /* _PLSR_TAGGED_INT32_ */

